"""错误处理和重试机制
提供智能的错误处理和重试策略
"""
import asyncio
import logging
import time
from typing import Callable, Optional, Any, Dict, List
from functools import wraps
from datetime import datetime, timedelta
from enum import Enum

logger = logging.getLogger(__name__)


class ErrorType(Enum):
    """错误类型枚举"""
    NETWORK = "network"  # 网络错误
    TIMEOUT = "timeout"  # 超时错误
    RATE_LIMIT = "rate_limit"  # 频率限制
    AUTH = "auth"  # 认证错误
    PARSE = "parse"  # 解析错误
    SERVER = "server"  # 服务器错误
    UNKNOWN = "unknown"  # 未知错误


class RetryStrategy(Enum):
    """重试策略枚举"""
    EXPONENTIAL = "exponential"  # 指数退避
    LINEAR = "linear"  # 线性退避
    FIXED = "fixed"  # 固定间隔
    FIBONACCI = "fibonacci"  # 斐波那契退避


class ErrorClassifier:
    """错误分类器"""
    
    @staticmethod
    def classify(error: Exception) -> ErrorType:
        """分类错误类型
        
        Args:
            error: 异常对象
            
        Returns:
            错误类型
        """
        error_str = str(error).lower()
        error_type_name = type(error).__name__.lower()
        
        # 网络错误
        if any(keyword in error_str for keyword in [
            'connection', 'connect', 'network', 'socket', 'dns'
        ]) or 'connectionerror' in error_type_name:
            return ErrorType.NETWORK
        
        # 超时错误
        if 'timeout' in error_str or 'timeout' in error_type_name:
            return ErrorType.TIMEOUT
        
        # 频率限制
        if any(keyword in error_str for keyword in [
            'rate limit', 'too many requests', '429', 'throttl'
        ]):
            return ErrorType.RATE_LIMIT
        
        # 认证错误
        if any(keyword in error_str for keyword in [
            'unauthorized', 'forbidden', '401', '403', 'auth'
        ]):
            return ErrorType.AUTH
        
        # 解析错误
        if any(keyword in error_str for keyword in [
            'parse', 'decode', 'json', 'xml', 'html'
        ]) or 'decodeerror' in error_type_name:
            return ErrorType.PARSE
        
        # 服务器错误
        if any(keyword in error_str for keyword in [
            '500', '502', '503', '504', 'server error'
        ]):
            return ErrorType.SERVER
        
        return ErrorType.UNKNOWN


class RetryCalculator:
    """重试计算器"""
    
    @staticmethod
    def calculate_delay(
        attempt: int,
        strategy: RetryStrategy = RetryStrategy.EXPONENTIAL,
        base_delay: float = 1.0,
        max_delay: float = 60.0
    ) -> float:
        """计算重试延迟
        
        Args:
            attempt: 尝试次数（从1开始）
            strategy: 重试策略
            base_delay: 基础延迟（秒）
            max_delay: 最大延迟（秒）
            
        Returns:
            延迟时间（秒）
        """
        if strategy == RetryStrategy.EXPONENTIAL:
            delay = base_delay * (2 ** (attempt - 1))
        elif strategy == RetryStrategy.LINEAR:
            delay = base_delay * attempt
        elif strategy == RetryStrategy.FIXED:
            delay = base_delay
        elif strategy == RetryStrategy.FIBONACCI:
            # 计算斐波那契数列
            a, b = base_delay, base_delay
            for _ in range(attempt - 1):
                a, b = b, a + b
            delay = a
        else:
            delay = base_delay
        
        # 添加随机抖动（±10%）
        import random
        jitter = delay * random.uniform(-0.1, 0.1)
        delay += jitter
        
        # 限制最大延迟
        return min(delay, max_delay)


class SmartRetryHandler:
    """智能重试处理器"""
    
    def __init__(
        self,
        max_retries: int = 3,
        base_delay: float = 1.0,
        max_delay: float = 60.0,
        strategy: RetryStrategy = RetryStrategy.EXPONENTIAL
    ):
        """初始化重试处理器
        
        Args:
            max_retries: 最大重试次数
            base_delay: 基础延迟（秒）
            max_delay: 最大延迟（秒）
            strategy: 重试策略
        """
        self.max_retries = max_retries
        self.base_delay = base_delay
        self.max_delay = max_delay
        self.strategy = strategy
        self.classifier = ErrorClassifier()
        self.calculator = RetryCalculator()
        
        # 错误类型对应的重试策略
        self.error_strategies = {
            ErrorType.NETWORK: (5, RetryStrategy.EXPONENTIAL),
            ErrorType.TIMEOUT: (3, RetryStrategy.LINEAR),
            ErrorType.RATE_LIMIT: (10, RetryStrategy.EXPONENTIAL),
            ErrorType.AUTH: (1, RetryStrategy.FIXED),
            ErrorType.PARSE: (2, RetryStrategy.FIXED),
            ErrorType.SERVER: (5, RetryStrategy.EXPONENTIAL),
            ErrorType.UNKNOWN: (3, RetryStrategy.EXPONENTIAL)
        }
    
    def should_retry(self, error: Exception, attempt: int) -> bool:
        """判断是否应该重试
        
        Args:
            error: 异常对象
            attempt: 当前尝试次数
            
        Returns:
            是否应该重试
        """
        error_type = self.classifier.classify(error)
        max_retries, _ = self.error_strategies.get(error_type, (self.max_retries, self.strategy))
        
        # 认证错误通常不需要重试
        if error_type == ErrorType.AUTH:
            logger.warning("Authentication error, not retrying")
            return False
        
        # 解析错误重试意义不大
        if error_type == ErrorType.PARSE and attempt > 1:
            logger.warning("Parse error persists, not retrying")
            return False
        
        return attempt <= max_retries
    
    def get_retry_delay(self, error: Exception, attempt: int) -> float:
        """获取重试延迟
        
        Args:
            error: 异常对象
            attempt: 当前尝试次数
            
        Returns:
            延迟时间（秒）
        """
        error_type = self.classifier.classify(error)
        _, strategy = self.error_strategies.get(error_type, (self.max_retries, self.strategy))
        
        # 频率限制需要更长的延迟
        if error_type == ErrorType.RATE_LIMIT:
            base_delay = self.base_delay * 10
            max_delay = self.max_delay * 2
        else:
            base_delay = self.base_delay
            max_delay = self.max_delay
        
        return self.calculator.calculate_delay(
            attempt=attempt,
            strategy=strategy,
            base_delay=base_delay,
            max_delay=max_delay
        )
    
    async def execute_with_retry(
        self,
        func: Callable,
        *args,
        on_retry: Optional[Callable] = None,
        **kwargs
    ) -> Any:
        """执行函数并自动重试
        
        Args:
            func: 要执行的函数
            *args: 函数参数
            on_retry: 重试时的回调函数
            **kwargs: 函数关键字参数
            
        Returns:
            函数执行结果
        """
        last_error = None
        
        for attempt in range(1, self.max_retries + 1):
            try:
                # 执行函数
                if asyncio.iscoroutinefunction(func):
                    return await func(*args, **kwargs)
                else:
                    return func(*args, **kwargs)
                    
            except Exception as e:
                last_error = e
                error_type = self.classifier.classify(e)
                
                logger.warning(
                    f"Attempt {attempt} failed with {error_type.value} error: {e}"
                )
                
                # 判断是否应该重试
                if not self.should_retry(e, attempt):
                    break
                
                # 计算延迟
                delay = self.get_retry_delay(e, attempt)
                
                logger.info(f"Retrying after {delay:.2f} seconds...")
                
                # 执行重试回调
                if on_retry:
                    if asyncio.iscoroutinefunction(on_retry):
                        await on_retry(attempt, e, delay)
                    else:
                        on_retry(attempt, e, delay)
                
                # 等待
                await asyncio.sleep(delay)
        
        # 所有重试都失败
        logger.error(f"All {self.max_retries} retries failed")
        raise last_error


def with_smart_retry(
    max_retries: int = 3,
    base_delay: float = 1.0,
    strategy: RetryStrategy = RetryStrategy.EXPONENTIAL
):
    """智能重试装饰器
    
    Args:
        max_retries: 最大重试次数
        base_delay: 基础延迟（秒）
        strategy: 重试策略
    """
    def decorator(func):
        @wraps(func)
        async def async_wrapper(*args, **kwargs):
            handler = SmartRetryHandler(
                max_retries=max_retries,
                base_delay=base_delay,
                strategy=strategy
            )
            return await handler.execute_with_retry(func, *args, **kwargs)
        
        @wraps(func)
        def sync_wrapper(*args, **kwargs):
            handler = SmartRetryHandler(
                max_retries=max_retries,
                base_delay=base_delay,
                strategy=strategy
            )
            return asyncio.run(handler.execute_with_retry(func, *args, **kwargs))
        
        if asyncio.iscoroutinefunction(func):
            return async_wrapper
        else:
            return sync_wrapper
    
    return decorator


class CircuitBreaker:
    """熔断器
    用于防止持续失败的请求
    """
    
    def __init__(
        self,
        failure_threshold: int = 5,
        recovery_timeout: int = 60,
        expected_exception: type = Exception
    ):
        """初始化熔断器
        
        Args:
            failure_threshold: 失败阈值
            recovery_timeout: 恢复超时（秒）
            expected_exception: 期望的异常类型
        """
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.expected_exception = expected_exception
        
        self.failure_count = 0
        self.last_failure_time: Optional[datetime] = None
        self.state = 'closed'  # closed, open, half_open
    
    def call(self, func: Callable, *args, **kwargs) -> Any:
        """通过熔断器调用函数
        
        Args:
            func: 要执行的函数
            *args: 函数参数
            **kwargs: 函数关键字参数
            
        Returns:
            函数执行结果
        """
        # 检查熔断器状态
        if self.state == 'open':
            if self._should_attempt_reset():
                self.state = 'half_open'
            else:
                raise Exception("Circuit breaker is open")
        
        try:
            result = func(*args, **kwargs)
            self._on_success()
            return result
        except self.expected_exception as e:
            self._on_failure()
            raise e
    
    async def async_call(self, func: Callable, *args, **kwargs) -> Any:
        """通过熔断器异步调用函数
        
        Args:
            func: 要执行的异步函数
            *args: 函数参数
            **kwargs: 函数关键字参数
            
        Returns:
            函数执行结果
        """
        # 检查熔断器状态
        if self.state == 'open':
            if self._should_attempt_reset():
                self.state = 'half_open'
            else:
                raise Exception("Circuit breaker is open")
        
        try:
            result = await func(*args, **kwargs)
            self._on_success()
            return result
        except self.expected_exception as e:
            self._on_failure()
            raise e
    
    def _should_attempt_reset(self) -> bool:
        """判断是否应该尝试重置
        
        Returns:
            是否应该尝试重置
        """
        return (
            self.last_failure_time and
            (datetime.now() - self.last_failure_time).seconds >= self.recovery_timeout
        )
    
    def _on_success(self):
        """成功时的处理"""
        self.failure_count = 0
        self.state = 'closed'
    
    def _on_failure(self):
        """失败时的处理"""
        self.failure_count += 1
        self.last_failure_time = datetime.now()
        
        if self.failure_count >= self.failure_threshold:
            self.state = 'open'
            logger.warning(f"Circuit breaker opened after {self.failure_count} failures")
    
    def reset(self):
        """重置熔断器"""
        self.failure_count = 0
        self.last_failure_time = None
        self.state = 'closed'
        logger.info("Circuit breaker reset")


class ErrorRecorder:
    """错误记录器
    记录和分析错误模式
    """
    
    def __init__(self, max_records: int = 1000):
        """初始化错误记录器
        
        Args:
            max_records: 最大记录数
        """
        self.max_records = max_records
        self.records: List[Dict[str, Any]] = []
        self.error_counts: Dict[str, int] = {}
    
    def record(self, error: Exception, context: Dict[str, Any] = None):
        """记录错误
        
        Args:
            error: 异常对象
            context: 上下文信息
        """
        error_type = ErrorClassifier.classify(error)
        
        record = {
            'timestamp': datetime.now(),
            'error_type': error_type.value,
            'error_message': str(error),
            'error_class': type(error).__name__,
            'context': context or {}
        }
        
        self.records.append(record)
        
        # 限制记录数
        if len(self.records) > self.max_records:
            self.records = self.records[-self.max_records:]
        
        # 更新错误计数
        error_key = f"{error_type.value}:{type(error).__name__}"
        self.error_counts[error_key] = self.error_counts.get(error_key, 0) + 1
    
    def get_error_patterns(self, time_window: timedelta = None) -> Dict[str, Any]:
        """获取错误模式分析
        
        Args:
            time_window: 时间窗口
            
        Returns:
            错误模式分析结果
        """
        if time_window:
            cutoff_time = datetime.now() - time_window
            recent_records = [
                r for r in self.records
                if r['timestamp'] > cutoff_time
            ]
        else:
            recent_records = self.records
        
        if not recent_records:
            return {}
        
        # 分析错误类型分布
        type_distribution = {}
        for record in recent_records:
            error_type = record['error_type']
            type_distribution[error_type] = type_distribution.get(error_type, 0) + 1
        
        # 找出最常见的错误
        most_common = sorted(
            self.error_counts.items(),
            key=lambda x: x[1],
            reverse=True
        )[:10]
        
        return {
            'total_errors': len(recent_records),
            'type_distribution': type_distribution,
            'most_common': most_common,
            'time_range': {
                'start': recent_records[0]['timestamp'] if recent_records else None,
                'end': recent_records[-1]['timestamp'] if recent_records else None
            }
        }
    
    def suggest_strategy(self) -> Dict[str, Any]:
        """基于错误模式建议策略
        
        Returns:
            策略建议
        """
        patterns = self.get_error_patterns(timedelta(hours=1))
        suggestions = []
        
        if not patterns:
            return {'suggestions': ['No recent errors']}
        
        type_dist = patterns.get('type_distribution', {})
        
        # 网络错误多 -> 建议增加重试和使用代理
        if type_dist.get('network', 0) > 10:
            suggestions.append('Consider using proxy or increasing network timeout')
        
        # 频率限制多 -> 建议降低请求频率
        if type_dist.get('rate_limit', 0) > 5:
            suggestions.append('Reduce request frequency or implement better rate limiting')
        
        # 超时错误多 -> 建议增加超时时间
        if type_dist.get('timeout', 0) > 5:
            suggestions.append('Increase timeout duration')
        
        # 认证错误 -> 建议检查凭证
        if type_dist.get('auth', 0) > 0:
            suggestions.append('Check authentication credentials')
        
        return {
            'patterns': patterns,
            'suggestions': suggestions
        }